package com.baymax.test.scene.demo

import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageView
import androidx.core.content.FileProvider
import com.baymax.test.scene.BaseScene
import com.baymax.test.scene.addButton
import com.baymax.test.scene.addDFView
import com.baymax.test.scene.newSceneFlexContainer
import com.facebook.binaryresource.FileBinaryResource
import com.facebook.cache.common.CacheKey
import com.facebook.common.executors.UiThreadImmediateExecutorService
import com.facebook.common.references.CloseableReference
import com.facebook.common.util.UriUtil
import com.facebook.datasource.BaseDataSubscriber
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.*
import com.facebook.drawee.drawable.ForwardingDrawable
import com.facebook.imagepipeline.cache.*
import com.facebook.imagepipeline.common.*
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.drawable.DrawableFactory
import com.facebook.imagepipeline.image.*
import com.facebook.imagepipeline.memory.*
import com.facebook.imagepipeline.request.*
import com.google.gson.Gson
import io.reactivex.functions.Consumer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.coroutines.suspendCoroutine
import kotlin.properties.Delegates

/**
 * @title: 标题
 * @project: TestScene
 * @package: com.baymax.test.scene.demo
 * @class: LineDrawableSscene
 * @description:
 *
 *
 * 类描述
 *
 *
 * @version: 1.0
 * @createDate: 2022/8/16 12:35
 * @Copyright (C) 2022 YSTEN
 * @author: xuyujian
 */
class FrescoScene : BaseScene() {
    val url =
        "http://images.center.bcs.ottcn.com:8080/images/ysten/picture/ncos/cmjs/2021/12/29/4cdd4cf1222a4ed1b52409675e9049fc.jpg"

    override fun onCreateView(
        layoutInflater: LayoutInflater,
        viewGroup: ViewGroup,
        bundle: Bundle?
    ): View {


        val tracker = object : PoolStatsTracker {
            override fun setBasePool(basePool: BasePool<*>?) {
                Log.d(TAG, "setBasePool: $basePool");
            }

            override fun onValueReuse(bucketedSize: Int) {
                Log.d(TAG, "onValueReuse: $bucketedSize");
            }

            override fun onSoftCapReached() {
                Log.d(TAG, "onSoftCapReached:");
            }

            override fun onHardCapReached() {
                Log.d(TAG, "onHardCapReached:");
            }

            override fun onAlloc(size: Int) {
                Log.d(TAG, "onAlloc:$size");
            }

            override fun onFree(sizeInBytes: Int) {
                Log.d(TAG, "onFree:$sizeInBytes");
            }

            override fun onValueRelease(sizeInBytes: Int) {
                Log.d(TAG, "onValueRelease:$sizeInBytes");
            }
        }
        val build = PoolConfig.newBuilder()
            .setBitmapPoolStatsTracker(tracker)
            .setNativeMemoryChunkPoolStatsTracker(tracker)
            .build()

        Log.d(TAG, "bitmapPoolParams:${build.bitmapPoolParams.toJson()}");
        Log.d(TAG, "nativeMemoryChunkPoolParams:${build.nativeMemoryChunkPoolParams.toJson()}");
        Log.d(TAG, "flexByteArrayPoolParams:${build.flexByteArrayPoolParams.toJson()}");
        Log.d(TAG, "smallByteArrayPoolParams:${build.smallByteArrayPoolParams.toJson()}");
        Fresco.initialize(
            requireActivity(),
            ImagePipelineConfig.newBuilder(requireActivity())
                .setPoolFactory(
                    PoolFactory(
                        build
                    )
                )
                .build()
        )

        return viewGroup.newSceneFlexContainer {
            val scene = this@FrescoScene
            val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
                .setResizeOptions(ResizeOptions(250, 250))
                .build()


            val cacheKey =
                DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(request, context)
            val image = ImageView(context).apply {
                layoutParams = MarginLayoutParams(250, 250)
                scaleType = ImageView.ScaleType.MATRIX
                addView(this)
            }
            addButton("load file from disk") {
                launch(Dispatchers.IO) {
                    Log.d(TAG, "load file from disk:1 ")
                    suspendCoroutine<Unit> {
                        Fresco.getImagePipeline().prefetchToDiskCache(request, scene)
                            .subscribe(object : BaseDataSubscriber<Void>() {
                                override fun onNewResultImpl(dataSource: DataSource<Void>?) {
                                    Log.d(TAG, "load file from disk:2 ")
                                    it.resumeWith(Result.success(Unit))
                                }

                                override fun onFailureImpl(dataSource: DataSource<Void>?) {
                                    it.resumeWith(Result.failure(Throwable("error")))
                                }
                            }, Dispatchers.IO.asExecutor())
                    }
                    Log.d(TAG, "load file from disk:3 ")
                    while (!loadDisk(request, cacheKey)) {
                        delay(10)
                    }
                }
                launch {
//                    Fresco.getImagePipelineFactory().mainFileCache.insert(cacheKey ){
//                        Log.d(TAG, "mainFileCache: insert $it")
//                    }
                }
            }

            addButton("load encode image") {
                launch(Dispatchers.IO) {
                    Log.d(TAG, "load encode image1: ")
                    Fresco.getImagePipeline().fetchEncodedImage(request, scene).result.apply {
                        Log.d(TAG, "fetchEncodedImage: $this")
                        CloseableReference.closeSafely(this)

                    }
                    Log.d(TAG, "load encode image2: ")
                    while (!loadDisk(request, cacheKey)) {
                        delay(10)
                    }
                }

            }
            addButton("load encode image2") {
                launch(Dispatchers.IO) {
                    Log.d(TAG, "load encode image1: ")
                    val r = Fresco.getImagePipeline().fetchEncodedImage(request, scene)
                    do {
                        Log.d(TAG, "progress:${r.progress} ")
                    } while (!r.isFinished)
//                    val inputStream: InputStream = PooledByteBufferInputStream(load!!.get())
//                    Fresco.getImagePipelineFactory().mainFileCache.insert(cacheKey){
//                        Okio.buffer(Okio.sink(it)).writeAll(Okio.source(inputStream))
//                    }
                    CloseableReference.closeSafely(r.result)
                    Log.d(TAG, "load encode image2: ")
                    while (!loadDisk(request, cacheKey)) {
                        delay(10)
                    }
                }
            }

            addButton("load drawable") {
                val r = Fresco.getImagePipeline().fetchDecodedImage(request, null)
                r.subscribe(object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
                    override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>?) {
                        Log.d(TAG, "onNewResultImpl: $dataSource")
                        val rr = dataSource?.result ?: return
                        image.setImageBitmap((rr.get() as CloseableStaticBitmap).underlyingBitmap)
                    }

                    override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>?) {
                        Log.d(TAG, "onFailureImpl: $dataSource")
                    }

                }, Dispatchers.Main.asExecutor())
            }

            addButton("remove") {
                Fresco.getImagePipeline().evictFromDiskCache(request)
            }


            addButton("provider") {

                launch {
                    runCatching {
                        val f =
                            (Fresco.getImagePipelineFactory().mainFileCache.getResource(cacheKey) as? FileBinaryResource)?.file!!
                        Log.d(TAG, "provider :$f ")
                        val uriFile = FileProvider.getUriForFile(
                            requireSceneContext(),
                            context.packageName + ".fileprovider",
                            f
                        )
                        Log.d(TAG, "provider: $uriFile")
//                        val url = "content://tv.icntv.ott.fileprovider/cache/image_cache/v2.ols100.1/3/DrPTYDQcNLTediv6xu1xwQwRh2k.cnt"
//                        val uri = Uri.parse(url)
                        val input =
                            requireSceneContext().contentResolver.openInputStream(uriFile)!!
                        image.setImageBitmap(BitmapFactory.decodeStream(input))
                    }.onFailure {
                        Log.w(TAG, "error", it)
                    }

                }

            }
            val testV = object : View(context) {
                override fun drawableStateChanged() {
                    super.drawableStateChanged()
                }

                val collect = 0xff

                var collectState: Boolean by Delegates.observable(false) { p, old, new ->
                    Log.d(TAG, "collect: $new")
                   refreshDrawableState()
                }
                val stateDrawable = StateListDrawable().apply {
                    addState(
                        intArrayOf(collect, android.R.attr.state_selected),
                        loadFromUrl(UriUtil.getUriForResourceId(android.R.drawable.ic_menu_add).toString(),ColorDrawable(Color.RED))
                    )
                    addState(
                        intArrayOf(-collect, android.R.attr.state_selected),
                        loadFromUrl(UriUtil.getUriForResourceId(android.R.drawable.ic_menu_view).toString(),ColorDrawable(Color.GREEN))
                    )

                    addState(intArrayOf(), loadFromUrl(UriUtil.getUriForResourceId(android.R.drawable.ic_media_ff).toString(),ColorDrawable(Color.BLUE)))
                }

                private fun loadFromUrl(
                    url: String,
                    drawable: ColorDrawable
                ): Drawable {
                    return ForwardingDrawable(drawable).apply {
                        loadDrawable(
                            requireSceneContext(),
                            url,
                            { this.drawable = it } ,
                            { this.drawable = drawable}
                        )
                    }
                }

                init {
                    background = stateDrawable
                }
                override fun onCreateDrawableState(extraSpace: Int): IntArray? {
                    val drawableState = super.onCreateDrawableState(extraSpace + 1)
                    if (collectState) {
                        mergeDrawableStates(drawableState, intArrayOf(collect))
                    } else {

                    }
                    return drawableState
                }

            }
            val bg = addDFView(100, 100 , testV)


            addButton("pick select") {
                bg.isSelected = !bg.isSelected
            }
            addButton("pick collect") {
                testV.collectState = !testV.collectState
            }
        }
    }

    private fun loadDisk(imageRequest: ImageRequest, cacheKey: CacheKey): Boolean {
        val hasKey = Fresco.getImagePipelineFactory().mainFileCache.hasKey(cacheKey)
        Log.d(TAG, "loadDisk: hasKey $hasKey")
        if (hasKey) {
            val f =
                (Fresco.getImagePipelineFactory().mainFileCache.getResource(cacheKey) as? FileBinaryResource)?.file
            Log.d(TAG, "loadDisk: file $f")
            return f != null

        }
        return false

    }

    fun loadDrawable(
        context: Context?,
        url: String?,
        imageConsumer: Consumer<Drawable?>,
        error: Consumer<Throwable?>
    ) {
        val imageRequest = ImageRequest.fromUri(url)
        val closeableReferenceDataSource =
            Fresco.getImagePipeline().fetchDecodedImage(imageRequest, null)
        closeableReferenceDataSource.subscribe(object :
            BaseDataSubscriber<CloseableReference<CloseableImage?>?>() {
            override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage?>?>) {
                try {
                    val result = dataSource.getResult()
                    val closeableImage = result!!.get()
                    if (closeableImage != null) {
                        if (closeableImage is CloseableAnimatedImage) {
                            val animatedDrawableFactory: DrawableFactory =
                                Fresco.getImagePipelineFactory().getAnimatedDrawableFactory(context)
                                    ?: error("not find drawable Factory")
                            val drawable: Drawable? =
                                animatedDrawableFactory.createDrawable(closeableImage)
                            imageConsumer.accept(drawable)
                        } else if (closeableImage is CloseableBitmap) {
                            val closeableBitmap = closeableImage
                            val bitmap = closeableBitmap.getUnderlyingBitmap()
                            if (bitmap != null && !bitmap.isRecycled()) {
                                // https://github.com/facebook/fresco/issues/648
                                val tempBitmap = bitmap.copy(bitmap.getConfig(), false)
                                imageConsumer.accept(BitmapDrawable(tempBitmap))
                            }
                        }
                    }
                } catch (e: Exception) {
                    try {
                        error.accept(e)
                    } catch (ex: Exception) {
                    }
                }
            }

            override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage?>?>) {
                try {
                    error.accept(dataSource.getFailureCause())
                } catch (e: Exception) {
                }
            }
        }, UiThreadImmediateExecutorService.getInstance())
    }
}


private val g = Gson()
private fun PoolParams.toJson(): String = g.toJson(this)
